#include "common.h"
#include "memory/cache.h"
#include <stdlib.h>
#include <stdio.h>
#include "burst.h"
#include "time.h"

extern void ddr3_read2(hwaddr_t addr, void *data);
extern void ddr3_write2(hwaddr_t addr, void *data, uint8_t *mask);
extern void dram_write(hwaddr_t addr, size_t len, uint32_t data);

uint64_t timee;//

void cache_init() 
{
	int i;
	timee = 0;
    i=0;
	for(i = 0; i < CACHEL1_SETSIZE * CACHEL1_WAYSIZE; i++) 
        cache1[i].valid = false;


	for(i = 0; i < CACHEL2_SETSIZE * CACHEL2_WAYSIZE; i++) 
            {cache2[i].valid = false;
            cache2[i].dirty = false;
            }
}

void add_time(uint32_t tm)
{
    timee+=tm;
}

int cache1_read(hwaddr_t addr)
{
    uint32_t tag = addr>>(CACHEL1_BLSIZE_BIT+CACHEL1_SET_BIT);
    uint32_t temp =1<<CACHEL1_SET_BIT;
    uint32_t set = (addr>>(CACHEL1_BLSIZE_BIT))&(temp-1);

    int i,j,z;
    i=0,j=0,z=0;
    for(i=CACHEL1_WAYSIZE*set;i<CACHEL1_WAYSIZE*(set+1);i++)//获得第set组中的块
    {
        if((tag==cache1[i].tag)&&(cache1[i].valid))
        {
            return i;
        }
    }

    j=cache2_read(addr);//若没有返回则在二级cache中找
    srand(i);//设置随机数种子
    z=rand();
    i=CACHEL1_WAYSIZE*set+z%CACHEL1_WAYSIZE;//随机替换，且不能到下一组去
    memcpy(cache1[i].data, cache2[j].data, CACHEL1_BLSIZE);//从l2放入l1中进行替换
    cache1[i].valid=true;
    cache1[i].tag=tag;
    return i;
}

int cache2_read(hwaddr_t addr)
{
    uint32_t temp1 =CACHEL2_BLSIZE_BIT+CACHEL2_SET_BIT;
    uint32_t tag = addr>>temp1;
    
    uint32_t temp2 =(1<<CACHEL2_SET_BIT)-1;
    uint32_t set = (addr>>(CACHEL2_BLSIZE_BIT))&temp2;

    uint32_t temp3 =addr >> CACHEL1_BLSIZE_BIT;
    uint32_t block = temp3 << CACHEL1_BLSIZE_BIT;////////////

    uint32_t temp4=CACHEL2_BLSIZE_BIT + CACHEL2_SET_BIT;
    uint32_t temp5=set << CACHEL2_BLSIZE_BIT;
    

    int i,j,z;
    i=0,j=0,z=0;
    for(i=CACHEL2_WAYSIZE*set;i<CACHEL2_WAYSIZE*(set+1);i++)//获得第set组中的块
    {
        if((tag==cache2[i].tag)&&(cache2[i].valid))
        {
            return i;
        }
    }

    srand(i);
    z=rand();


	i = CACHEL2_WAYSIZE * set + z % CACHEL2_WAYSIZE;

	if(cache2[i].dirty && cache2[i].valid) {//若被替换的块已被修改则先写回
		uint32_t block2 = (cache2[i].tag << temp4) |temp5;


		uint8_t mask[BURST_LEN * 2];
        memset(mask, 1, BURST_LEN * 2);//////
        
		for (j = 0; j < CACHEL2_BLSIZE / BURST_LEN; j++) {
			ddr3_write2(block2 + j * BURST_LEN, cache2[i].data + j * BURST_LEN, mask);//写回内存，后写前
		}
	}

	for(j = 0; j < CACHEL2_BLSIZE / BURST_LEN; j++)
    {
		ddr3_read2(block + j * BURST_LEN , cache2[i].data + j * BURST_LEN);//进行随机写入，前写后
	}

	cache2[i].valid = true;
	cache2[i].tag = tag;
	cache2[i].dirty = false;//刚被替换，脏位置0
	return i;

}

void cache1_write(hwaddr_t addr, size_t len, uint32_t data)
{
     uint32_t temp1 =CACHEL1_BLSIZE_BIT+CACHEL1_SET_BIT;
    uint32_t tag = addr>>temp1;

    uint32_t temp2 =(1<<CACHEL1_SET_BIT)-1;
    uint32_t set = (addr>>(CACHEL1_BLSIZE_BIT))&temp2;

    uint32_t off=addr&(CACHEL1_BLMASK);

    int i;
    i=0;

    for(i=CACHEL1_WAYSIZE*set;i<CACHEL1_WAYSIZE*(set+1);i++)
    {
        if(cache1[i].valid&&cache1[i].tag==tag)//write hit
        {
            if(off + len <= CACHEL1_BLSIZE)
            {
                dram_write(addr, len, data);//写通
                memcpy(cache1[i].data + off, &data, len);//写入
				cache2_write(addr, len, data);//再更新cache2
			}
            else if(off + len > CACHEL1_BLSIZE) {//写出界，存在CACHEL1中的只有BLSIZE-off长
                dram_write(addr, CACHEL1_BLSIZE - off, data);	//write through
				memcpy(cache1[i].data + off, &data, CACHEL1_BLSIZE - off);
                
				cache2_write(addr, CACHEL1_BLSIZE - off, data);//update cache2
				cache1_write(addr + CACHEL1_BLSIZE - off, len - CACHEL1_BLSIZE + off, data >> (CACHEL1_BLSIZE - off));
			} 
            
			return;
        }
    }
    cache2_write(addr, len, data);//写未命中，在cache2中进行写操作
}

void cache2_write(hwaddr_t addr, size_t len, uint32_t data)
{
    uint32_t temp1 =CACHEL2_BLSIZE_BIT+CACHEL2_SET_BIT;
    uint32_t tag = addr>>temp1;

    uint32_t temp2 =(1<<CACHEL2_SET_BIT)-1;
    uint32_t set = (addr>>(CACHEL2_BLSIZE_BIT))&temp2;
    
    uint32_t off=addr&(CACHEL2_BLMASK);

    int i,j;
    i=0,j=0;
    for(i=CACHEL2_WAYSIZE*set;i<CACHEL1_WAYSIZE*(set+1);i++)
    {
        if(cache2[i].valid&&cache2[i].tag==tag)//写命中
        {
            if(off+len<=CACHEL2_BLSIZE_BIT)
            {
                memcpy(cache2[i].data+off,&data,len);
            }
            else{
                memcpy(cache2[i].data + off, &data, CACHEL2_BLSIZE - off);
				cache2_write(addr + CACHEL2_BLSIZE - off, len - CACHEL2_BLSIZE + off, data >> (CACHEL2_BLSIZE - off));
            }
            cache2[i].dirty=1;
        return;
        }
    }
    j=cache2_read(addr);//写未命中,write allocate

    cache2[j].dirty = 1;
    
	memcpy(cache2[j].data + off, &data, len);
}

